﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Diagnostics;
using NATUPNPLib;

namespace GShare1._0.Server
{
    public delegate void MessageRelayDelegate(string msg);
    public class Server
    {
        public Socket _serverSocket;
        public List<Client> clientList = new List<Client>();
        public MessageBoxInvoker _invoker;
        private ServerState state;
        public ClientListUpdateDelegate updateListInvoker;
        private bool isOnline;
        private MessageRelayDelegate messageRelayInvoker;
        private UPnPNATClass nat;

        private string password;

        public string Password
        {
            get { return password; }
            set { password = value; }
        }
        

        public Server(MessageBoxInvoker invoker,ClientListUpdateDelegate listInvoker)
        {
            _invoker = invoker;
            updateListInvoker = listInvoker;
            messageRelayInvoker = new MessageRelayDelegate(Message);
            this.isOnline = false;
            nat = new UPnPNATClass();
        }

        internal void Start()
        {
            try
            {

                //Socket creation,binding and listen mode
                _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _serverSocket.Bind(new IPEndPoint(0,3333));
                //set socket to listen state
                _serverSocket.Listen(0);
                //begin to accept connections from clients
                _invoker.BeginInvoke("Server Started", null, null);
                
///PROBLEM WITH SOCKET BEING BOUND

                _serverSocket.BeginAccept(_serverSocket.ReceiveBufferSize,new AsyncCallback(AcceptCallback), null);
                NATUPNPLib.IStaticPortMappingCollection mappings = nat.StaticPortMappingCollection;
                mappings.Add(3333, "TCP", 3333, LocalIPAddress(), true, "Gshare");
                this.isOnline = true;



            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ex.TargetSite);

            }
        }

        private void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                if (isOnline)
                {

                    state = new ServerState();
                    state.Client = _serverSocket;
                    ClientInfo info = new ClientInfo();
                    byte[] buffer = new byte[_serverSocket.ReceiveBufferSize];
                    //Create Client from the IAsyncResult (ar)
                    Client client = new Client(_serverSocket.EndAccept(out buffer,ar), _invoker, info, this,messageRelayInvoker);
                    
                    //add password SYSTEM HERE
                    client.clientInfo.UserName = Encoding.ASCII.GetString(buffer);
                    client.clientInfo.ConnectionTime = DateTime.Now;
                    
                    //Invoke
                    _invoker.BeginInvoke(client.clientInfo.UserName +" Connected", null, null);
                    messageRelayInvoker.BeginInvoke(client.clientInfo.UserName + " Connected", null, null);

                    //using the client socket start receiving
                    //Here all client operations get handeld inside client
                    client._clientSocket.BeginReceive(client.Buffer, 0, client.Buffer.Length, SocketFlags.None, new AsyncCallback(client.ReceiveCallback), state);
                    //add client to the connected list

                    clientList.Add(client);
                    updateListInvoker.BeginInvoke(clientList, null, null);



                    //Create Async loop to keep handling incomming connections
                    _serverSocket.BeginAccept(_serverSocket.ReceiveBufferSize,new AsyncCallback(AcceptCallback), null);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        internal void Shutdown()
        {
            if (isOnline)
            {
                this.isOnline = false;
                if (clientList.Count != 0)
                {
                    foreach (Client client in clientList.ToList())
                    {
                        client.Disconnect();
                    }


                }
                //call syncronously
                _invoker("Server Stopped");


                clientList.Clear();
                this._serverSocket.Close();

                //Remove the UPNP forward
                NATUPNPLib.IStaticPortMappingCollection mappings = nat.StaticPortMappingCollection;
                try
                {
                    mappings.Remove(3333, "TCP");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.TargetSite);
                }
            }
            
            
        }

        internal void Message(string msg)
        {
            foreach (Client client in clientList.ToList())
            {
                MessageSender.SendText(msg, client._clientSocket);
            }
        }

        public string LocalIPAddress()
        {
            IPHostEntry host;
            string localIP = "";
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIP = ip.ToString();
                    break;
                }
            }
            return localIP;
        }

        


    }
}
